/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package KeyServer;

/**
 *
 * @author John
 */
import cryptography.Aes;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.SecureRandom;
import java.sql.SQLException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.codec.digest.DigestUtils;

import mysql.MysqlHandler;
import cryptography.DiffieHellman;
import java.math.BigInteger;

/**
 * Handles client connection requests.
 */
public class ConnectionRequestHandler implements Runnable {

    private Socket _socket = null;
    private PrintWriter _out = null;
    private BufferedReader _in = null;

    char[] charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray();

    int duration;

    public ConnectionRequestHandler(Socket socket, int duration) {

        _socket = socket;
        this.duration = duration;

    }

    public void run() {

        System.out.println("Client connected to socket: " + _socket.toString());

        try {
            _out = new PrintWriter(_socket.getOutputStream(), true);
            _in = new BufferedReader(new InputStreamReader(_socket.getInputStream()));

            String buffer, imei, challengeResponse, challengeResult, clientQuant, typeOfService;
            int challenge;

            DiffieHellman dh = new DiffieHellman();
            dh.initiate();
            BigInteger primeNumber = dh.getPrimeNumber();
            BigInteger base = dh.getBase();
            BigInteger quantity = dh.getQuantity();

            _out.println(primeNumber);
            _out.println(base);
            _out.println(quantity);

            clientQuant = _in.readLine();
            BigInteger clientQuantity = new BigInteger(clientQuant);

            BigInteger key = dh.calculateKey(clientQuantity);

            Aes aes = new Aes();

            byte[] key64 = aes.convertKeyMD5(key);

            buffer = _in.readLine();
            typeOfService = aes.decrypt(buffer, key64);

            if (typeOfService.equals("challengeMe")) {

                buffer = _in.readLine();
                imei = aes.decrypt(buffer, key64);
                System.out.println("imei : " + imei);

                if (checkImei(imei)) {

                    buffer = aes.encrypt("valid", key64);
                    _out.println(buffer);

                    challenge = getToken(imei);

                    challengeResult = calculateChallenge(imei, challenge);

                    buffer = aes.encrypt(Integer.toString(challenge), key64);
                    _out.println(buffer);
                    _out.flush();

                    buffer = _in.readLine();

                    challengeResponse = aes.decrypt(buffer, key64);

                    if (challengeResponse.equals(challengeResult)) {

                        String registeredTempPass = checkTempPass(imei);

                        if (registeredTempPass != null) {

                            int remainingTime = getRemainingTime(imei);

                            buffer = aes.encrypt("exist", key64);
                            _out.println(buffer);
                            buffer = aes.encrypt(registeredTempPass, key64);
                            _out.println(buffer);
                            buffer = aes.encrypt(Integer.toString(remainingTime), key64);
                            _out.println(buffer);
                            _out.close();
                            _in.close();
                            _socket.close();

                        } else {

                            String tempPass = randomString(charset, 7);

                            saveTempPass(tempPass, imei, duration);

                            buffer = aes.encrypt(tempPass, key64);
                            _out.println(buffer);
                            buffer = aes.encrypt(Integer.toString(duration), key64);
                            _out.println(buffer);

                            TempPassHandler tph = new TempPassHandler(imei, duration);
                            tph.run();

                            _out.close();
                            _in.close();
                            _socket.close();
                        }
                    } else {

                        _out.println("error");
                        _out.println("Error: Wrong Password");
                        _out.close();
                        _in.close();
                        _socket.close();

                    }

                } else {

                    _out.println("error");
                    _out.println("Error: Imei is not Registered");
                    _out.close();
                    _in.close();
                    _socket.close();

                }
            } else if (typeOfService.equals("register")) {

                String email, password;

                buffer = _in.readLine();
                imei = aes.decrypt(buffer, key64);
                System.out.println("imei : " + imei);
                buffer = _in.readLine();
                email = aes.decrypt(buffer, key64);
                System.out.println("email : " + email);
                buffer = _in.readLine();
                password = aes.decrypt(buffer, key64);
                System.out.println("password : " + password);

                int result = registerUser(imei, email, password);

                buffer = aes.encrypt(Integer.toString(result), key64);
                _out.println(buffer);

                _out.close();
                _in.close();
                _socket.close();

            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(ConnectionRequestHandler.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SQLException ex) {
            Logger.getLogger(ConnectionRequestHandler.class.getName()).log(Level.SEVERE, null, ex);
        } finally { //In case anything goes wrong we need to close our I/O streams and sockets.
            try {
                _out.close();
                _in.close();
                _socket.close();
            } catch (Exception e) {
                System.out.println("Couldn't close I/O streams");
            }
        }
    }

    public int registerUser(String imei, String email, String password) throws SQLException, ClassNotFoundException {

        MysqlHandler mysql = new MysqlHandler();

        mysql.createConnection();

        int result = mysql.chechAvailability(imei, email);

        if (result == 1) {
            mysql.registerUser(password, imei, email);
        }

        mysql.closeConnection();

        return result;

    }

    public static String randomString(char[] characterSet, int length) {

        Random random = new SecureRandom();
        char[] result = new char[length];

        for (int i = 0; i < result.length; i++) {
            // picks a random index out of character set > random character
            int randomCharIndex = random.nextInt(characterSet.length);
            result[i] = characterSet[randomCharIndex];
        }

        return new String(result);
    }

    public String checkTempPass(String imei) throws SQLException, ClassNotFoundException {

        MysqlHandler mysql = new MysqlHandler();

        String tempPass;

        mysql.createConnection();

        tempPass = mysql.checkTempPass(imei);

        mysql.closeConnection();

        return tempPass;

    }

    public boolean checkImei(String imei) throws ClassNotFoundException, SQLException {

        MysqlHandler mysql = new MysqlHandler();

        boolean isRegistered;

        mysql.createConnection();
        isRegistered = mysql.checkRegistered(imei);

        mysql.closeConnection();

        return isRegistered;

    }

    public String calculateChallenge(String imei, int token) throws ClassNotFoundException, SQLException {

        MysqlHandler mysql = new MysqlHandler();

        String password;
        String challenge;

        mysql.createConnection();

        password = mysql.getPassword(imei);

        challenge = imei + password;

        for (int i = 0; i < token; i++) {
            challenge = DigestUtils.sha1Hex(challenge);
        }

        mysql.closeConnection();

        return challenge;

    }

    public int getToken(String imei) throws ClassNotFoundException, SQLException {

        MysqlHandler mysql = new MysqlHandler();

        int token;

        mysql.createConnection();

        token = mysql.getCurrentToken(imei);

        mysql.closeConnection();

        return token;

    }

    public void saveTempPass(String tempPass, String imei, int duration) throws ClassNotFoundException, SQLException {

        MysqlHandler mysql = new MysqlHandler();

        mysql.createConnection();

        mysql.registerTempPass(tempPass, imei, duration);

        mysql.closeConnection();

    }

    public int getRemainingTime(String imei) throws ClassNotFoundException, SQLException {

        int time;

        MysqlHandler mysql = new MysqlHandler();

        mysql.createConnection();

        time = mysql.getCurrentTokenRemainingTime(imei);

        mysql.closeConnection();

        return time;

    }
}
